#region Microsoft Public License
/*
 * (c) Copyright Chenjun Wu. http://utablesdk.codeplex.com
 * 
 * This source is subject to the Microsoft Public License (Ms-PL).
 * Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
 * All other rights reserved.
 */
#endregion

﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UTable.Input.MultiTouch;
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Threading;
using UTable.Objects.Controls;
using System.Diagnostics;

namespace UTable.Objects.Handlers
{
    public class OneFingerGestureHandler : CertainFingerGestureHandler
    {
        #region Private Fields

		Point _originalFingerPosition;

		Point _originalDragPosition;

        long _FingerDownTimeTick;

        Point _previousFingerPosition;

        UIElement _relativeElement;

        DispatcherTimer _holdTimer;

		PhysicsTracker _physicsTracker = new PhysicsTracker();

		#endregion

        #region Private Methods

        void _holdTimer_Tick(object sender, EventArgs e)
        {
            if (_holdTimer != null)
            {
                _holdTimer.Stop();
				_holdTimer = null;

                if (GestureDetected != null && this.EnabledGestures.Contains(SingleFingerGestureType.Holding))
                    GestureDetected(this, new SingleFingerGestureEventArgs(SingleFingerGestureType.Holding, _previousFingerPosition));
            }
        }

        #endregion

        #region Properties

        public double MaxTapDistance { get; set; }

        public SingleFingerStateType State { get; set; }

		public bool TrackVelocity
		{ 
			get { return _physicsTracker.TrackVelocity; }
			set { _physicsTracker.TrackVelocity = value; } 
		}

		public TimeSpan MinHoldTime { get; set; }

		public List<SingleFingerGestureType> EnabledGestures { get; set; }

		#endregion

		#region Constructors

        public OneFingerGestureHandler(IControl node, UIElement relativeElement)
			: base(node)
		{
			_relativeElement = relativeElement;
			State = SingleFingerStateType.Hold;

			// initialize the variables
			EnabledGestures = new List<SingleFingerGestureType>();
			EnabledGestures.Add(SingleFingerGestureType.Tap);
			EnabledGestures.Add(SingleFingerGestureType.DragStarted);
			EnabledGestures.Add(SingleFingerGestureType.Dragging);
			EnabledGestures.Add(SingleFingerGestureType.DragEnded);
            MaxTapDistance = 20;
			TrackVelocity = false;
			MinHoldTime = TimeSpan.FromMilliseconds(2000);
		}

		#endregion


        public override int FingerNumber
        {
            get { return 1; }
        }

        public override void OnCertainFingersDown(Collection<FingerEventArgs> fingers)
        {
            Debug.Assert(fingers.Count == 1, "Move than one fingers on OneFingerGestureHandler!");

            Point p = (Node as UIElement).TranslatePoint(fingers[0].Position, _relativeElement);
            _originalFingerPosition = _previousFingerPosition = p;
            _FingerDownTimeTick = fingers[0].Timestamp;
            State = SingleFingerStateType.Hold;

            // start the hold timer
            if (this.EnabledGestures.Contains(SingleFingerGestureType.Holding))
            {
                _holdTimer = new DispatcherTimer();
                _holdTimer.Tick += new EventHandler(_holdTimer_Tick);
                _holdTimer.Interval = MinHoldTime;
                _holdTimer.Start();
            }
        }

        public override void OnCertainFingersMove(Collection<FingerEventArgs> fingers)
        {
            Point p = (Node as UIElement).TranslatePoint(fingers[0].Position, _relativeElement);
            if (State == SingleFingerStateType.Hold)
            {
                if ((fingers[0].Position - this._originalFingerPosition).Length > MaxTapDistance)
                {
                    // Begin Dragged
                    // try to capture the finger
                    if (Node.CaptureFinger(fingers[0].FingerID))
                    {
                        this.State = SingleFingerStateType.Drag;
                        _originalDragPosition = _previousFingerPosition;
                        if (GestureDetected != null && this.EnabledGestures.Contains(SingleFingerGestureType.DragStarted))
                            GestureDetected(this, new SingleFingerGestureDragEventArgs(SingleFingerGestureType.DragStarted, p, _previousFingerPosition, _originalDragPosition, _physicsTracker.UpdateVelocity(p, fingers[0].Timestamp)));

                        // stop holding if needed
                        if (_holdTimer != null)
                        {
                            _holdTimer.Stop();
                            _holdTimer = null;
                        }
                    }
                }
            }
            else if (State == SingleFingerStateType.Drag)
            {
                if (GestureDetected != null && this.EnabledGestures.Contains(SingleFingerGestureType.Dragging))
                {
                    GestureDetected(this, new SingleFingerGestureDragEventArgs(SingleFingerGestureType.Dragging, p, _previousFingerPosition, _originalDragPosition, _physicsTracker.UpdateVelocity(p, fingers[0].Timestamp)));
                }
            }
            _previousFingerPosition = p;
        }

        public override void OnCertainFingersUp(Collection<FingerEventArgs> fingers)
        {
            Point p = (Node as UIElement).TranslatePoint(fingers[0].Position, _relativeElement);

            // stop holding if needed
            if (_holdTimer != null)
            {
                _holdTimer.Stop();
                _holdTimer = null;
            }

            // raise click event if needed
            if (EnabledGestures.Contains(SingleFingerGestureType.Click))
            {
                if (Node is FrameworkElement)
                {
                    FrameworkElement element = Node as FrameworkElement;
                    if (fingers[0].Position.X >= 0 && fingers[0].Position.Y >= 0 && fingers[0].Position.X <= element.ActualWidth && fingers[0].Position.Y <= element.ActualHeight)
                    {
                        if (GestureDetected != null)
                            GestureDetected(this, new SingleFingerGestureEventArgs(SingleFingerGestureType.Click, p));
                    }
                }
            }


            if (State == SingleFingerStateType.Hold)
            {
                // raise tap event
                if (GestureDetected != null && this.EnabledGestures.Contains(SingleFingerGestureType.Tap))
                    GestureDetected(this, new SingleFingerGestureTapEventArgs(p));
            }
            else if (State == SingleFingerStateType.Drag)
            {
                // release the finger capture
                Node.UncaptureFinger(fingers[0].FingerID);

                // release drag event
                if (GestureDetected != null && this.EnabledGestures.Contains(SingleFingerGestureType.DragEnded))
                    GestureDetected(this, new SingleFingerGestureDragEventArgs(SingleFingerGestureType.DragEnded, p, _previousFingerPosition, _originalDragPosition, _physicsTracker.UpdateVelocity(p, fingers[0].Timestamp)));
            }
        }

        public override void OnCertainFingersCancelled(Collection<FingerEventArgs> fingers)
        {
            Point p = (Node as UIElement).TranslatePoint(fingers[0].Position, _relativeElement);

            // stop holding if needed
            if (_holdTimer != null)
            {
                _holdTimer.Stop();
                _holdTimer = null;
            }

            if (State == SingleFingerStateType.Drag)
            {
                // release drag event
                if (GestureDetected != null && this.EnabledGestures.Contains(SingleFingerGestureType.DragEnded))
                    GestureDetected(this, new SingleFingerGestureDragEventArgs(SingleFingerGestureType.DragEnded, p, _previousFingerPosition, _originalDragPosition, new Vector(0, 0)));
            }
        }

        #region Events

        public event OneFingerGestureEventHandler GestureDetected;

        #endregion
    }

    public delegate void OneFingerGestureEventHandler(OneFingerGestureHandler sender, SingleFingerGestureEventArgs args);
}
